package svc;

import constants.Constants;
import dao.MilkDao;
import domain.milk.Milk;
import domain.milk.SeasonalityMilk;
import domain.milk.WoodsCurves;

public class MilkSvc {
	MilkDao dao = new MilkDao();
	
	public Milk getMilkDetails(int fileNo){
		Milk result = new Milk();
		
		SeasonalityMilk seasonalityMilk = dao.getSeasonality(fileNo);
		WoodsCurves woodsUser = dao.getWoodsCurves(fileNo);
		result.setMilkYieldLact(dao.getMilkYieldLact());
		result.setMilkYieldPreg(dao.getMilkYieldPreg());
		
		/*double lbs = Constants.KG;
		if(unit == 1){
			lbs = Constants.KG;
		}else{
			lbs = Constants.LBS;
		}*/
		int unit = 1;					// For calculation purpose we will always have unit as KG
		
		result.setSeasonalityMilk(seasonalityMilk);
		result.setWoodCurves(woodsUser);
		
		result.setMilk(getLactationMilkTable(seasonalityMilk, woodsUser, unit));
		
		
		return result;
	}
	
	
	public double[][][] getLactationMilkTable(SeasonalityMilk seasonalityMilk, WoodsCurves woodsUser, int lbs){
		double mlk[][][] = new double[3+1][Constants.MIM+1][Constants.SEA+1];
		
		//WoodsCurves woodsCalc = woodsUser;
		WoodsCurves woods = processWoods(woodsUser);
		
		for(int lact=1; lact<=3; lact++){
			double mAbs = seasonalityMilk.getAbsAdjustment()[lact];
			double mPct = seasonalityMilk.getPctAdjustment()[lact];
			//double mSeasonality = seasonalityMilk.getOverallSeasonality()[lact];
			double mSea[] = seasonalityMilk.getMonthlySeasonality()[lact];
			//double mSeaAvg = getMonthlySeasonalityAvg(seasonalityMilk.getMonthlySeasonality()[lact]);
			
			for(int sea=1; sea<=Constants.SEA; sea++){
				for(int mim=1; mim<=Constants.MIM; mim++){
					double days = ((mim-1)*30.4)+15.2;
					//mlk[lact][mim][sea] = ((mSeasonality * woodsCalc.getLactA()[lact] * Math.pow(days, woodsCalc.getLactB()[lact]) * Math.exp(-woodsCalc.getLactC()[lact] * days) * mSea[sea]) + ((1 - mSeasonality) * woodsCalc.getLactA()[lact] * Math.pow(days, woodsCalc.getLactB()[lact]) * Math.exp(-woodsCalc.getLactC()[lact] * days) * mSeaAvg)) * (1 + mPct) * lbs + mAbs;
					//=X$5*((W10/30.4)^X$6)*EXP((-X$7*W10)/30.4)
					mlk[lact][mim][sea] = woods.getLactA()[lact] * Math.pow((days/30.4), woods.getLactB()[lact]) * Math.exp((-1 * woods.getLactC()[lact]) * (days/30.4));
					mlk[lact][mim][sea] = mlk[lact][mim][sea] * mSea[sea];
					mlk[lact][mim][sea] = mlk[lact][mim][sea] * (1 + mPct);
					mlk[lact][mim][sea] = mlk[lact][mim][sea] + mAbs;
				}
			}
		}
		
		return mlk;
	}

	@SuppressWarnings("unused")
	private double getMonthlySeasonalityAvg(double[] monthlySea){
		double sum = 0;
		for(int i=1; i<=Constants.SEA; i++){
			sum += monthlySea[i];
		}
		return sum/Constants.SEA;
	}
	
	
	public boolean updateMilkDetails(int fileNo, Milk milk){
		boolean result1 = dao.updateWoodsCurves(fileNo, milk.getWoodCurves());
		boolean result2 = dao.updateSeasonality(fileNo, milk.getSeasonalityMilk());
		return (result1 && result2);
	}
	
	public WoodsCurves processWoods(WoodsCurves woodsUser) {
		WoodsCurves result = new WoodsCurves();
		
		for(int lact=1; lact<4; lact++){
			result.getLactA()[lact] = getWoodA(woodsUser.getLactA()[lact], woodsUser.getLactB()[lact], woodsUser.getLactC()[lact]);
			result.getLactB()[lact] = getWoodB(woodsUser.getLactB()[lact], woodsUser.getLactC()[lact]);
			result.getLactC()[lact] = getWoodC(woodsUser.getLactB()[lact], woodsUser.getLactC()[lact]);
		}
		return result;
	}
	
	private double getWoodA(double woodUserA, double woodUserB, double woodUserC){
		double woodC = getWoodC(woodUserB, woodUserC);
		double woodB = getWoodB(woodUserB, woodUserC);
		double result = woodUserA * Math.pow((woodC/woodB) , woodB) * Math.exp(woodB);
		return result;
	}
	
	private double getWoodB(double woodUserB, double woodUserC){
		double woodC = getWoodC(woodUserB, woodUserC);
		double result = woodC * (woodUserB/30.4);
		return result;
	}
	
	private double getWoodC(double woodUserB, double woodUserC){
		double result = (-1 * woodUserC) * ((woodUserB + 305)/(woodUserB - 305));
		return result;
	}
	
}